<?php
/**
 * @author    jan huang <bboyjanhuang@gmail.com>
 * @copyright 2016
 *
 * @link      https://www.github.com/janhuang
 * @link      http://www.fast-d.cn/
 */
namespace Server;

if (config()->get('ws_host')) {
    define('REQUST_HOST', config()->get('ws_host'));
} elseif (config()->get('environment')==='dev') {
    define('REQUST_HOST', '0.0.0.0:9579');
} else {
    define('REQUST_HOST', 'ydapi.linghit.com');
}
define('REQUEST_URL', '/chat_room/index');

error_reporting(E_ALL ^ E_NOTICE);

use FastD\Swoole\Server\WebSocket;
use FastD\Logger\Logger;

class WsServer extends WebSocket
{
    protected $server;
    protected $request;
    protected $frame;
    protected $fd;
    protected $from_uid;
    protected $to_uid;
    protected $room_id;

    /**
     * 连接成功
     */
    public function doOpen(\swoole_websocket_server $server, \swoole_http_request $request)
    {
        $this->server = $request;
        $this->debug('连接 ' . $request->fd . ' 成功');
    }

    /**
     * 断开连接
     */
    public function doClose(\swoole_server $server, $fd, $fromId)
    {
        list($res, $statusCode) = $this->post(REQUST_HOST . REQUEST_URL, ['reqdata'=> json_encode([
                                            'type' => 'clientClose',
                                            'client_id' => $fd,
                                            'time' => time(),
                                        ]),
                                        'client_id' => $fd] );
        $this->debug(json_decode($res, true));
    }

    /**
     * 接收消息
     */
    public function doMessage(\swoole_server $server, \swoole_websocket_frame $frame)
    {
        $this->server = $server;
        $this->frame = $frame;
        $this->fd = $frame->fd;

        if ($frame->finish==1) {

            $requestData = json_decode($frame->data, true);
            $this->from_uid = $requestData['from_uid'];
            $this->to_uid = $requestData['to_uid'];
            $this->room_id = $requestData['room_id'];
            
            try {
                list($res, $statusCode) = $this->post(REQUST_HOST . REQUEST_URL, ['reqdata' => $frame->data, 'client_id' => $frame->fd] );
                $this->handleResponse($requestData, $res);
                        
            } catch (Exception $e) {
                //出错处理 应该要记录Log错误信息
                $this->send('serverConnectError', $requestData['data'], '', '0049', $e->getMessage());
                $logger = new Logger('websocket');
                $logger->log(Logger::ERROR, $res);
            }     

            
        }else{
            $this->debug("数据不完整!");
        }

    }

    /**
     * 处理http返回数据
     * @return [type] [description]
     */
    public function handleResponse($requestData, $res)
    {
        $server = $this->server;
        $frame = $this->frame;

        $res_arr = json_decode($res, true);
        $this->debug($res_arr);

        //http报错
        if ($res_arr['code'] != 1) {
            $this->send('requestError', [], '', '0049', $res);
            $this->debug($res);
            return;
        }

        $response = $res_arr['data'];
        switch ($response['type']) {

            case 'bind':
                //绑定uid 和 client_id
                if( $server->bind($frame->fd, $response['from_uid']) ){
                    $this->send('bind', '0001', '绑定成功');
                }else{
                    $this->send('bind', '1000', '绑定失败');
                }

                //查看用户是否在线
                $isOnline = false;
                if (isset($response['to_uid'])) {
                    foreach ($server->connections as $fd) {
                        if ($fd == $response['to_uid']) {   //在线
                            $isOnline = true;
                            break;
                        }
                    }
                }

                if ($isOnline) {
                    $this->send('isToUserOnline', [], '', '0001', '对方在线' );
                    $this->send('isToUserOnline', [], $response['to_uid'], '0001', '对方上线');

                }else{
                    $this->send('isToUserOnline', [], '', '0002', '对方不在线' );
                }

                $this->send('roomInfo', $response['data'], '', '0001', '聊天室信息');
                break;
            case 'blank':   //不做任何处理
                
                break;
            default:
                if ( isset($response['from_uid']) ) {
                    $response['data']['is_me'] = 1;
                    $this->send($response['type'], $response['data'], '', $response['code'], $response['msg']);
                }


                //发送消息给对方
                if ( isset($response['to_uid']) ) {
                    $isOnline = false;
                    foreach ($server->connections as $fd) {
                        if ($fd == $response['to_uid']) {   //在线
                            $isOnline = true;
                            break;
                        }
                    }

                    if ($isOnline) {
                        $response['data']['is_me'] = 0;
                        if(!$this->send($response['type'], $response['data'], $response['to_uid'], $response['code'], $response['msg'])){           //发送失败
                            $this->errorCallback($requestData, $response, $frame, 'sendMsgError', '发送消息失败');
                        }

                    }
                    
                }

                //回调给http
                if (isset($response['callback'])) {
                    $this->callback($requestData, $response, $frame);
                }

        }
    }


    /**
     * 发送消息
     * @param  [type] $type  [请求的方法名]
     * @param  [type] $data  [请求数据]
     * @param  string $toUid [发送到的uid 默认是$fd]
     * @param  string $code  [状态码]
     * @param  string $msg   [提示信息]
     */
    private function send($type, $data=[], $toUid='', $code='0001', $msg='成功')
    {
        $toUid = (int)$toUid ? : $this->fd;

        return $this->server->push($toUid, json_encode([
                    'type' => $type,
                    'code' => $code,
                    'msg' => $msg,
                    'time' => time(),
                    'data' => $data,
                ]));

    }

    /**
     *请求服务端控制器
     */
    private function post($url, $params, array $headers = [])
    {
        $this->debug($url);
        $this->debug($params);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);

        $response = [];
        $response[] = curl_exec($ch);
        $response[] = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        $this->debug(sprintf('curl code:%s error:%s', curl_errno($ch), curl_error($ch)));

        if (curl_errno($ch) > 0) {
            throw new \Exception(curl_error($ch), curl_errno($ch));
        }

        curl_close($ch);
        return $response;
    }

    //出错回调
    private function errorCallback($requestData, $response, $frame, $type, $errorMsg)
    {
        $this->send($type, $response['data'], '', '0009', $errorMsg);
        //失败回调
        $response['data']['room_id'] = $requestData['room_id'];
        $response['data']['from_uid'] = $requestData['from_uid'];
        $response['data']['to_uid'] = $requestData['to_uid'];
        $response['data']['request_action'] = $response['type'];
        list($res, $statusCode) = $this->post(REQUST_HOST . REQUEST_URL, ['reqdata'=> json_encode([
                                            'type' => $type,
                                            'room_id' => $requestData['room_id'],        
                                            'from_uid' => $requestData['from_uid'],         //要用加密的from_uid
                                            'to_uid' => $requestData['to_uid'],             //要用加密的to_uid
                                            'time' => time(),
                                            'data' => $response['data'],
                                        ]),
                                        'client_id' => $frame->fd] );

        $this->debug(json_decode($res, true));
    }

    //正常回调
    private function callback($requestData, $response, $frame)
    {

        $callback = $response['callback'];
        foreach ($callback as $key => $value) {

            //定时执行一次
            if (isset($value['setTimeOut'])) {

                $timer = swoole_timer_after($value['setTimeOut'] * 1000, function ($timer_id )use($requestData, $requestData, $frame, $value) {

                    list($res, $statusCode) = $this->post(REQUST_HOST . REQUEST_URL, ['reqdata'=> json_encode([
                                                        'type' => $value['type'],
                                                        'room_id' => $requestData['room_id'],        
                                                        'from_uid' => $requestData['from_uid'],         //要用加密的from_uid
                                                        'to_uid' => $requestData['to_uid'],             //要用加密的to_uid
                                                        'time' => time(),
                                                        'data' => $value['data'],
                                                    ]),
                                                    'client_id' => $frame->fd] );
                    $this->handleResponse($requestData, $res);

                }, []);

            }else{

                list($res, $statusCode) = $this->post(REQUST_HOST . REQUEST_URL, ['reqdata'=> json_encode([
                                                    'type' => $value['type'],
                                                    'room_id' => $requestData['room_id'],        
                                                    'from_uid' => $requestData['from_uid'],         //要用加密的from_uid
                                                    'to_uid' => $requestData['to_uid'],             //要用加密的to_uid
                                                    'time' => time(),
                                                    'data' => $value['data'],
                                                ]),
                                                'client_id' => $frame->fd] );

                $this->handleResponse($requestData, $res);
            }

      
        }

    }

    /**
     * 调试输出
     * @param  [array/string] $data [输出内容]
     */
    private function debug($data)
    {
        if (config()->get('is_debug')) {
            if (is_array($data)) {
                print_r($data);
            }else{
                echo $data;
            }
            echo "\n";
        }
    }


}
